#define CONFIG_FILE	"dnfs-server.conf"
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <dirent.h>
#include <fcntl.h>
#include <math.h>
//#include <netinet/in.h>
#include <arpa/inet.h>
#include "dnfs.h"
#include "common.h"
#include "server.h"

extern struct nodelist *nodehead;
extern struct filelist *filehead;
pthread_mutex_t nodelistlock = PTHREAD_MUTEX_INITIALIZER;
unsigned int TS; //Trip Size
uint64_t LS; //Low space
char METAROOT[NAMELEN];

/*
 * 目录直接用lstat里的stat信息；普通文件用文件中保存的DSTAT信息
 */
bool_t cs_lstat_2_svc(lstat_in * arg, lstat_out * res, struct svc_req * rqst)
{
	struct stat stbuf;
	char filename[NAMELEN];
	sprintf(filename, "%s%s", METAROOT, arg->filename);
	PDEBUG("[cs_lstat_2_svc] : %s\n", filename);	
	FILE *fp;

	res->res = lstat(filename, &stbuf);	
	if ( res->res == -1) {	
		res->res = -errno;
		printf("stat %s error: %s, errno = %d\n", filename, strerror(-res->res), -res->res);
		return TRUE;
	}
	
	if (S_ISREG(stbuf.st_mode)) {		
		if ((fp = fopen(filename, "r")) == NULL) {
			res->res = -1;
			printf("can't open %s\n", filename);
			return TRUE;
		}	
		fread(&res->stbuf, sizeof(struct DSTAT), 1, fp);
		fclose(fp);
	//	print_dstat(&(res->stbuf), arg->filename);
	}
	else if (S_ISDIR(stbuf.st_mode)) {
	//	print_stat(&stbuf, filename);
		res->stbuf.st_dev = stbuf.st_dev;
		res->stbuf.st_ino = stbuf.st_ino;
		res->stbuf.st_mode = stbuf.st_mode;
		res->stbuf.st_nlink = stbuf.st_nlink;
		res->stbuf.st_uid = stbuf.st_uid;
		res->stbuf.st_gid = stbuf.st_gid;
		res->stbuf.st_rdev = stbuf.st_rdev;
		res->stbuf.st_size = stbuf.st_size;
		res->stbuf.st_blksize = stbuf.st_blksize;
		res->stbuf.st_blocks = stbuf.st_blocks;
		res->stbuf.atime = stbuf.st_atime;
		res->stbuf.mtime = stbuf.st_mtime;
		res->stbuf.ctime = stbuf.st_ctime;
	}
//	PDEBUG("return dlstat,res = %d\n", res->res);	
//	print_dstat(&(res->stbuf), arg->filename);
	return TRUE;	
}

bool_t cs_access_2_svc(access_in *arg, int *res, struct svc_req *rqst)
{
	PDEBUG("[cs_access_2_svc]:%s\n", arg->filename);
	char path[NAMELEN];
	int mask;
	sprintf(path, "%s%s", METAROOT, arg->filename);
	mask = arg->mask;

	*res = access(path, mask);
	if (*res == -1)
		*res = -errno;	
	return TRUE;
}

bool_t cs_opendir_2_svc(opendir_in *arg, opendir_out *res, struct svc_req *rqst)
{
	char path[NAMELEN];
	sprintf(path, "%s%s", METAROOT, arg->filename);
	PDEBUG("[opendir]: %s\n", path);
	DIR *dp;
	dp = opendir(path);
	PDEBUG("dp = %p\n", dp);
//	long l = dp;
//	res->dp = l;//(long long)dp;
	res->dp = (uint64_t)dp;
//	PDEBUG("l = %ld, res->dp = %e\n", l, res->dp);
	if (res->dp == 0) {
		res->res = -errno;
		PDEBUG("dp == NULL\n");
	}
	return TRUE;
}

bool_t cs_readdir_2_svc(readdir_in *arg, readdir_out *res, struct svc_req *rqst)
{
	DIR *dp;
	struct dirent *de;
	dp = (DIR *)arg->dp;
	de = readdir(dp);
//	PDEBUG("[%s]\n", de->d_name);
	PDEBUG("[readdir_2_svc]: dp = %p\n", dp);

	res->d_name = (char *) malloc (NAMELEN);
	if (de != NULL) {
		res->d_ino = de->d_ino;
		res->d_type = de->d_type;
		strcpy(res->d_name, de->d_name);//d_name是相对路径名
//		PDEBUG("res->d_ino = %ld, de->d_ino = %ld, res->d_type = %d, de->d_type = %d, res->d_name = %s, de->d_name = %s\n", res->d_ino, de->d_ino, res->d_type, de->d_type, res->d_name, de->d_name);
		PDEBUG("res->d_name = %s\n", res->d_name);
	}
	else {//readdir have reach the end
		PDEBUG("readdir have reach the end\n");
		res->d_ino = res->d_type = 0;	
	}
	return TRUE;
}

bool_t cs_closedir_2_svc(readdir_in *arg, void *res, struct svc_req *rqst)
{
	DIR *dp;
	dp = (DIR *)arg->dp;
	PDEBUG("[closedir_2_svc]: dp = %p\n", dp);
	closedir(dp);
	return TRUE;
}

bool_t cs_mknod_2_svc(mknod_in *arg, int *res, struct svc_req *rqst)
{
	char path[NAMELEN];
	sprintf(path, "%s%s", METAROOT, arg->filename);
	mode_t mode = arg->mode;
	dev_t rdev = arg->rdev;
//	int res;
PDEBUG("[cs_mknod_2_svc]: path = %s, mode = %o, rdev = %ld\n", path, mode, (long)rdev);

	if (S_ISREG(mode)) {
        	*res = open(path, O_CREAT | O_EXCL | O_WRONLY, mode);
        	if (*res >= 0)
            		*res = close(*res);
		else {
			*res = -errno;
			return TRUE;
		}
		PDEBUG("create regular file, *res = %d\n", *res);

		//确保该文件不为空，在创建时就写入DSTAT和chunknum信息——open，mknod时
		struct stat s1;
		struct DSTAT s2;
		if (lstat(path, &s1) == -1) {
			*res = -errno;
			PDEBUG("lstat failed:%s\n", strerror(errno));
		}
		else {
			s2.st_dev = s1.st_dev;
			s2.st_ino = s1.st_ino;
			s2.st_mode = s1.st_mode;
			s2.st_nlink = s1.st_nlink;
			s2.st_uid = s1.st_uid;
			s2.st_gid = s1.st_gid;
			s2.st_rdev = s1.st_rdev;
			s2.st_size = 0;//s1.st_size;
			s2.st_blksize = s1.st_blksize;
			s2.st_blocks = s1.st_blocks;
			s2.atime = s1.st_atime;
			s2.mtime = s1.st_mtime;
			s2.ctime = s1.st_ctime;
		//	print_stat(&s1, path);

			FILE *fp;
			if ((fp = fopen(path, "w")) == NULL) {
				*res = -errno;
				printf("fopen %s failed: %s\n", path, strerror(errno));
				return TRUE;
			}
			fwrite(&s2, sizeof(s2), 1, fp);
			CHUNKNUM chunknum = 0;
			fwrite(&chunknum, sizeof(chunknum), 1, fp);
			fclose(fp);
		//	print_meta(path);
		}
    	} 
	else if (S_ISFIFO(mode))
        	*res = mkfifo(path, mode);
    	else
        	*res = mknod(path, mode, rdev);
    	if (*res == -1)
        	*res = -errno;
PDEBUG("res = %d\n", *res);
	return TRUE;
}

//正确时返回res=0；否则res=-errno
bool_t cs_open_2_svc(open_in *arg, open_out *res, struct svc_req *rqst)
{
	PDEBUG("[open_2_svc]: path = %s, flags = %#X, ip = %u\n", arg->filename, arg->mode, arg->ip);
	char path[NAMELEN];
	sprintf(path, "%s%s", METAROOT, arg->filename);
	int flags = arg->mode;

	res->res = 0;//The default result is succefully

	FILELIST *file = find_file(path);
	if (file != NULL) {//the file is opened now
		if ((flags & 0xF) == O_RDONLY) {
			if (file->wcount > 0) {
				res->res = -EAGAIN;
				print_file(file);	
				goto out;
			}
			else {
				file->rcount++;
				struct client *p = (struct client*) malloc (sizeof(CLIPER));
				p->ip = arg->ip;
				p->pre = p->next = NULL;
				insert_client(&file->rlist, p);
			}
		}
		else if ((flags & 0xF) == O_WRONLY || (flags & 0xF) == O_RDWR) {
			//if (file->rcount > 0 || file->wcount > 0) {//事实上不需要这一判断
			if (file->wcount != 1 || file->wip != arg->ip) {
				res->res = -EAGAIN;
				print_file(file);				
				goto out;
			}
			else {//should not get here
				printf("should never get here!\n");
				exit(1);
			}
		}
		else {
			printf("should never get here!\n");
			exit(-1);
		}
	}
	else {//the file is not opened now
		file = (FILELIST *) malloc (sizeof(FILELIST));
		strcpy(file->path, path);
		file->pre = file->next = NULL;
		file->rlist = NULL;

		struct client *p = (struct client*) malloc (sizeof(CLIPER));
		p->ip = arg->ip;
		p->pre = p->next = NULL;
		
		if ((flags & 0xF) == O_RDONLY) {
			file->rcount = 1;
			file->wcount = 0;
			file->wip = 0;
			insert_client(&file->rlist, p);
		}
		else if ((flags & 0xF) == O_WRONLY || (flags & 0xF) == O_RDWR) {
			file->rcount = 0;
			file->wcount = 1;	
			file->wip = arg->ip;
		}
		else {
			printf("should never get here!\n");
			exit(-1);
		}
		if ((res->res = insert_file(file)) != 0)
			goto out;
	}
	int fd;
    	fd = open(path, O_RDONLY);
	if (fd == -1) {
        	res->res = -errno;
		printf("open failed, res = %d: %s\n", errno, strerror(errno));
		goto out;
	}

	DSTAT stbuf;
	CHUNKNUM chunknum;
	if (read(fd, &stbuf, sizeof(stbuf)) <= 0) {// -1 and 0 are both error
		res->res = -errno;
		printf("read stbuf failed, res = %d: %s\n", errno, strerror(errno));
		close(fd);
		goto out;
	}
	if (read(fd, &chunknum, sizeof(chunknum)) <= 0) {// -1 and 0 are both error
		res->res = -errno;
		printf("read chunknum failed, res = %d: %s\n", errno, strerror(errno));
		close(fd);
		goto out;
	}
	res->filesize = stbuf.st_size;
	int count;
	res->buf.buf_len = chunknum * ICHUNK_SIZE;
	res->buf.buf_val = NULL;
	res->buf.buf_val = (char *) malloc (res->buf.buf_len);
	//lseek(fd, sizeof(stbuf) + sizeof(CHUNKNUM), SEEK_SET);//跳过CHUNKNUM
	if ((count = read(fd, res->buf.buf_val, res->buf.buf_len)) != res->buf.buf_len) {//把剩下所有东西读出来
		res->res = -errno;
		printf("read meta failed, count = %d, but chunknum = %d, expect %d, res = %d: %s\n", count, chunknum, res->buf.buf_len, errno, strerror(errno));
		if (res->buf.buf_val != NULL)
			free(res->buf.buf_val);
		close(fd);
		goto out;
	}
	//res->buf.buf_len = count;
	close(fd);

/********************
PDEBUG("buflen = %u\n", res->buf.buf_len);
CHUNK_MEM *tmp = (CHUNK_MEM *)res->buf.buf_val;
int x = res->buf.buf_len / ICHUNK_SIZE;
int i;
for (i = 0; i < x; i++) {
	PDEBUG("<%d> %s\t%u\t%u\n", i, tmp->oid, tmp->ip, tmp->size);
	tmp ++;
}
*********************/

out:
	if (res->res < 0) {
		res->filesize = 0;
		res->buf.buf_val = (char *)malloc (1);
		res->buf.buf_len = 0;
	}
print_filelist();
PDEBUG("res = %d\n", res->res);
	return TRUE;
}


/*
 * release包括了释放权限（lock），以及更新元数据两个方面
 * 1，读关闭：只需更改访问时间；
 * 2，写关闭但没有创建新chunk：改变修改时间，文件大小（如果变化的话），以及最后一块的大小；
 * 3，写关闭而且创建了新chunk：在2的基础上，添加新chunk的信息到元数据文件中。
 */
bool_t cs_release_2_svc(cs_release_in *arg, int *res, struct svc_req *rqst)
{
	DSTAT stbuf;
	char path[NAMELEN];
	sprintf(path, "%s%s", METAROOT, arg->path);
PDEBUG("[cs_release_svc]: filename = \"%s\", isdirty= %d, newsize = %lu\n", path, arg->isdirty, arg->newsize);
	int isdirty = arg->isdirty;
	*res = 0;

	FILELIST *file = find_file(path);
	if (file == NULL) {
		printf("Can't find the file in memory!\n");
		exit(-2);
	}
	//释放权限
	CLIPER *p = NULL;
	if ((p = find_client(file->rlist, arg->ip)) != NULL) {
		file->rcount--;
		delete_client(&file->rlist, p);//including free
	}
	else if (file->wip == arg->ip) {
		file->wcount--;
		file->wip = 0;
	}
	else {
		printf("Could not find the client %u in file %s\n", arg->ip, path);
		*res = -1;  
		return TRUE;
	}
print_file(file);
	if (file->rcount == 0 && file->wcount == 0) {
		delete_file(file);
	}
print_meta(path);
	FILE *fp = NULL;
	fp = fopen(path, "r+");
	if (fp == NULL) {
		*res  = -errno;
		printf("open %s failed: %s\n", path, strerror(errno));
		return TRUE;
	}
	fread(&stbuf, sizeof(struct DSTAT), 1, fp);
	time_t t;
	if (time(&t) == -1) {
		printf("get time failed: %s\n", strerror(errno));
	}
	if (!isdirty) {
		PDEBUG("open with 'r'\n");
		stbuf.atime = t;
	}
	else {//isdirty
		PDEBUG("open with 'w'\n");
		stbuf.mtime = t;
		//uint64_t oldsize = stbuf.st_size;
		uint64_t newsize = arg->newsize;
//		if (newsize > oldsize) {
			stbuf.st_size = arg->newsize;
			int n_chunknum;
			if (newsize == 0)
				n_chunknum = 0;
			else 
				n_chunknum = getcid(newsize - 1) + 1;//最大的块号+1为块数
			PDEBUG("n_chunknum = %d\n", n_chunknum);
		/*	int o_chunknum;////老的最大的块号
			Fseek(fp, sizeof(stbuf), SEEK_SET);
			//得到o_chunknum
			fread(&o_chunknum, sizeof(o_chunknum), 1, fp);
			o_chunknum--;
			uint32_t tmp;
			if (n_chunknum > o_chunknum) {//产生了新的chunk
				tmp = TS;
				Fseek(fp, sizeof(stbuf) + sizeof(o_chunknum) + ICHUNK_SIZE * o_chunknum + OIDLEN + IPLEN, SEEK_SET);//修改原最后一个chunk的大小
				fwrite(&tmp, sizeof(tmp), 1, fp);
				fwrite(arg->buf.buf_val, 1, arg->buf.buf_len, fp);//添加新的chunk的信息
			}
			else {
				tmp = newsize % TS;
				Fseek(fp, sizeof(stbuf) + sizeof(o_chunknum) + ICHUNK_SIZE * o_chunknum + OIDLEN + IPLEN, SEEK_SET);//修改原最后一个chunk的大小
				fwrite(&tmp, sizeof(tmp), 1, fp);
			}*/
			//目前的策略是全部chunk的元数据重新刷新一遍
			Fseek(fp, sizeof(stbuf), SEEK_SET);
			Fwrite(&n_chunknum, sizeof(n_chunknum), 1, fp);
			PDEBUG("buf_len = %u\n", arg->buf.buf_len);
			CHUNK_MEM* tmp = (CHUNK_MEM*)arg->buf.buf_val;
			int i;
			for(i = 0; i < n_chunknum; i++) {
				PDEBUG("%s\t%u\t%u\n", tmp->oid, tmp->ip, tmp->size);
				//tmp += ICHUNK_SIZE;
				tmp ++;
			}

			Fwrite(arg->buf.buf_val, arg->buf.buf_len, 1, fp);
//		}
	}
	rewind(fp);
	fwrite(&stbuf, sizeof(stbuf), 1, fp);
	fclose(fp);
print_meta(path);
PDEBUG("res = %d\n", *res);
	return TRUE;
}


/*
 * 得到新chunk的oid以及分配到的osd的ip，并不改变本地的元数据；等到close时才会刷新本地元数据；
 * 成功res->res = 0;否则为负数
 */

bool_t cs_newchunk_2_svc(cs_newchunk_in *arg, cs_newchunk_out *res, struct svc_req *rqst)
{
	char path[NAMELEN];
	sprintf(path, "%s%s", METAROOT, arg->path);
PDEBUG("[cs_newchunk_svc]: path = %s, pid = %u, ", path, arg->pid);
	struct nodelist* n;
	int pid = arg->pid;
	res->buf.buf_len = OIDLEN + sizeof(IP);
	res->buf.buf_val = (char *) malloc (res->buf.buf_len);
	if (getoid(path, pid, res->buf.buf_val) != 0) {
		printf("getoid failed\n");
		res->res = -1;
		return TRUE;
	}
PDEBUG("new oid = %s\n", res->buf.buf_val);
	//pthread_mutex_lock(&nodelistlock);
	n = nodehead;
	while (n->avail_space < LS && n != NULL)  {
		printf("osd %u is low_space: %ld\n", n->ip, n->avail_space);
		n = n->next;	
	}
	if (n == NULL) {
		printf("Can't find a osd whose avali_space > %ld\n", LS);
		res->res = -1;
	}
	else {
		memcpy(res->buf.buf_val + OIDLEN, &n->ip, sizeof(IP));
		print_nodelist();
		res->res = 0;	
	}
//	pthread_mutex_unlock(&nodelistlock);
PDEBUG("res = %d\n", res->res);
	return TRUE;
}


bool_t cs_unlink_2_svc(cs_unlink_in *arg, int *res, struct svc_req *rqst)
{
	PDEBUG("[unlink_2_svc]: path = %s\n", arg->filename);
	char path[NAMELEN];
	sprintf(path, "%s%s", METAROOT, arg->filename);
	struct so_unlink_in osdarg;
	FILE *fp = NULL;
	int chunknum;
	IP ipn;//ip in numeric.

	FILELIST *file = find_file(path);
	if (file != NULL/* && file->wip != arg->ip*/) {
		PDEBUG("The file %s is opened by other client now, can't unlink!\n", path);
		print_file(file);
		*res = -EAGAIN;
		return TRUE;
	}
	
	fp = fopen(path, "r");
	if (fp == NULL) {
		*res = -errno;
		printf("open %s failed, res = %d: %s\n", path, errno, strerror(errno));
		return TRUE;
	}
	if ((*res = Fseek(fp, sizeof(DSTAT), SEEK_SET)) < 0) {
		//res->res = err;
		exit(-1);
	}
	fread(&chunknum, sizeof(chunknum), 1, fp);
PDEBUG("chunknum = %d\n", chunknum);
	int i;
	CHUNK_MEM chunk;
	struct nodelist *osd;
	for (i = 0; i < chunknum; i++) {
		fseek(fp, sizeof(DSTAT) + sizeof(chunknum) + ICHUNK_SIZE * i, SEEK_SET);
		fread(&chunk, ICHUNK_SIZE, 1, fp);
		if (chunk.ip == 0)
			continue;
		osdarg.oid = chunk.oid;
		ipn = chunk.ip;
	//	pthread_mutex_lock(&nodelistlock);
		osd = find_node(ipn);//这里最好上锁，考虑到以后可能会对增加delete操作
		if (osd == NULL) {
		//	pthread_mutex_unlock(&nodelistlock);
			printf("Can't find the osd %u\n", ipn);
			*res = -1;
			goto out;
		}
		if (osd->clo == NULL) {
			CLIENT *cl;
			if ((cl = recreate_clo(osd->ip)) == NULL) {
		//		pthread_mutex_unlock(&nodelistlock);
				goto out;
			}
			else {
				osd->clo = cl;
			}
		}

	//	pthread_mutex_unlock(&nodelistlock);
		int err = so_unlink_2(&osdarg, res, osd->clo);
		if (err != RPC_SUCCESS) {
			char ipp[INET_ADDRSTRLEN];
			if (inet_ntop(AF_INET, &ipn, ipp, INET_ADDRSTRLEN) == NULL) {
				printf("inet_ntop failed\n");
				return -3;
			}
			clnt_perror(osd->clo, ipp);
			if (err == RPC_CANTSEND || err == ECONNRESET){// == 3,这时与osd的连接已经失效，重连)
		//		pthread_mutex_lock(&nodelistlock);
				PDEBUG("re-create osd handle\n");
				sleep(2);
				CLIENT *tmpcl = recreate_clo(osd->ip);//重新创建连接 
				if (tmpcl == NULL) {
		//			pthread_mutex_unlock(&nodelistlock);
					goto out;
				}
				clnt_destroy(osd->clo);//销毁原来的连接
				osd->clo = tmpcl;
		//		pthread_mutex_unlock(&nodelistlock);
				if (so_unlink_2(&osdarg, res, osd->clo) != RPC_SUCCESS) {
					printf("faild again\n");
					clnt_perror(osd->clo, ipp);
					goto out;
				}
			}
			else { //does not handle other error here
				//pthread_mutex_unlock(&osd->lock);
				goto out;
			}
		}
		if (*res < 0) {
			printf("osd unlink failed: %s\n", strerror(-*res));
			exit (-2);
		}
	}
	*res = unlink(path);
    	if (*res == -1)
        	 *res = -errno;
out:
	if (fp != NULL)
		fclose(fp);
PDEBUG("res = %d\n", *res);
    	return TRUE;
}

/*
 * 判断是否需要更像MDS上的元数据；对文件进行truncate操作
 */
bool_t cs_truncate_2_svc(cs_truncate_in *arg, int *res, struct svc_req *rqst)
{
	char path[NAMELEN];
	sprintf(path, "%s%s", METAROOT, arg->filename);
	off_t nsize = arg->truncate_size;
	off_t osize;
	DSTAT stbuf;
	int isdirty = arg->isdirty;
	CHUNKNUM ochunknum;
	FILE *fp = NULL;
PDEBUG("[truncate]: path = %s, isdirty = %d, size = %lu\n", arg->filename, arg->isdirty, arg->truncate_size);
//print_meta(path);

	FILELIST *file = find_file(path);
	if (file != NULL) {
		if (file->rcount == 1 && find_client(file->rlist, arg->ip) != NULL) {//只有该用户在读
			
		}
		else if (file->wip == arg->ip) {//只有该用户在写
			
		}
		else  {//有其他人在访问
			printf("The file %s is opened by other client now, can't truncate!\n", path);
			print_file(file);
			*res = -EAGAIN;
			return TRUE;
		}
	}

	//update the metadata of path if isdirty
	if (isdirty) {
		if (update_meta(path, arg->newsize, arg->buf.buf_val, arg->buf.buf_len) != 0)
			printf("update_meta failed\n");
	}
	
	fp = fopen(path, "r+");
    	if (fp == NULL) {
		*res  = -errno;//not -errno
		printf("open %s failed: %s\n", path, strerror(errno));
		return TRUE;
	}
	
	//前面在open，mknod时要确保该文件不为空——在创建时就写入DSTAT信息和chunknum
	if ((*res = Fread(&stbuf, sizeof(struct DSTAT), 1, fp)) != 0)
		goto out;

	osize = stbuf.st_size;
	if (osize != nsize) {
		stbuf.st_size = nsize;
		time_t t;
		if (time(&t) == -1) {
			printf("get time failed: %s\n", strerror(errno));
		}
		else {
			stbuf.mtime = t;
			stbuf.ctime = t;
		}

		rewind(fp);
		if ((*res = Fwrite(&stbuf, sizeof(stbuf), 1, fp)) != 0)
			goto out;
		//得到chunknum
		if ((*res = Fread(&ochunknum, sizeof(ochunknum), 1, fp)) != 0)
			goto out;
		PDEBUG("current chunknum = %d\n", ochunknum);
		//计算得到新的chunknum
		CHUNKNUM nchunknum;
		if (nsize == 0)
			nchunknum = 0;
		else 
			nchunknum = getcid(nsize - 1) + 1;
		if ((*res = Fseek(fp, sizeof(struct DSTAT), SEEK_SET)) < 0)
			goto out;
		//如果chunknum变小了，说明后面的chunk被删除了；变大了，则不用增加chunk
		if (nchunknum < ochunknum) {
			if ((*res = Fwrite(&nchunknum, sizeof(nchunknum), 1, fp)) != 0)
				goto out;
			fseek(fp, nchunknum * ICHUNK_SIZE, SEEK_CUR);
			CHUNK_MEM chunk;
			struct nodelist *osd;
			int i;
			struct so_unlink_in osdarg;
			IP ipn;
			for (i = 0; i < ochunknum - nchunknum; i++) {
				fread(&chunk, ICHUNK_SIZE, 1, fp);
				if (chunk.ip == 0)
					continue;
				osdarg.oid = chunk.oid;
				ipn = chunk.ip;
			//	pthread_mutex_lock(&nodelistlock);
				osd = find_node(ipn);//这里最好上锁，考虑到以后可能会对增加delete操作
				if (osd == NULL) {
				//	pthread_mutex_unlock(&nodelistlock);
					printf("Can't find the osd %u\n", ipn);
					*res = -1;
					goto out;
				}
				if (osd->clo == NULL) {
					CLIENT *cl;
					if ((cl = recreate_clo(osd->ip)) == NULL) {
					//	pthread_mutex_unlock(&nodelistlock);
						goto out;
					}
					else {
						osd->clo = cl;
					}
				}

			//	pthread_mutex_unlock(&nodelistlock);
				int err = so_unlink_2(&osdarg, res, osd->clo);
				if (err != RPC_SUCCESS) {
					char ipp[INET_ADDRSTRLEN];
					if (inet_ntop(AF_INET, &ipn, ipp, INET_ADDRSTRLEN) == NULL) {
						printf("inet_ntop failed\n");
						return -3;
					}
					clnt_perror(osd->clo, ipp);
					if (err == RPC_CANTSEND || err == ECONNRESET){// == 3,这时与osd的连接已经失效，重连)
					//	pthread_mutex_lock(&nodelistlock);
						PDEBUG("re-create osd handle\n");
						sleep(2);
						CLIENT *tmpcl = recreate_clo(osd->ip);//重新创建连接 
						if (tmpcl == NULL) {
						//	pthread_mutex_unlock(&nodelistlock);
							goto out;
						}
						clnt_destroy(osd->clo);//销毁原来的连接
						osd->clo = tmpcl;
					//	pthread_mutex_unlock(&nodelistlock);
						if ((err = so_unlink_2(&osdarg, res, osd->clo)) != RPC_SUCCESS) {
							printf("faild again\n");
							*res = -err;
							clnt_perror(osd->clo, ipp);
							goto out;
						}
					}
					else { //does not handle other error here
					//pthread_mutex_unlock(&osd->lock);
						*res = -err;
						goto out;
					}
				}
				if (*res < 0) {
					printf("osd unlink failed: %s\n", strerror(-*res));
					exit (-2);
				}
			}
		}
		if (nsize == 0)
			goto out;
		//如果文件是变小，只需改最后一个oss的大小；如果chunknum减少，则多出的那些oss记录可以不用管，因为chunknum已记录这些信息
		if (nsize < osize) {
			if ((*res = Fseek(fp, sizeof(struct DSTAT) + sizeof(CHUNKNUM) + ICHUNK_SIZE * (nchunknum - 1),  SEEK_SET)) < 0)
				goto out;
			//read the chunk first
			CHUNK_MEM chunk;
			if ((*res = Fread(&chunk, ICHUNK_SIZE, 1, fp)) < 0);
				goto out;
			//CHUNKSIZE psize = nchunknum % TS;
			chunk.size = nsize % TS;
			if ((*res = Fseek(fp, sizeof(struct DSTAT) + sizeof(CHUNKNUM) + ICHUNK_SIZE * (nchunknum - 1),  SEEK_SET)) < 0)
				goto out;
			if ((*res = Fwrite(&chunk, ICHUNK_SIZE, 1, fp)) < 0)
				goto out;
		}
		else{//文件变大，简单把size改大
			//printf("Truncate failed\n");
			//不需做任何事情
		}	
	}
out:
	if (fp != NULL) {
		fclose(fp);
	}
print_meta(path);
PDEBUG("res = %d\n", *res);
 	return TRUE;
}

/*
 * res = 0 or -EAGAIN;
 */
bool_t cs_update_permission_2_svc(cs_update_permission_in *arg, int *res, struct svc_req *rqst)
{
	PDEBUG("[update_permission]\n");
	char path[NAMELEN];
	sprintf(path, "%s%s", METAROOT, arg->filename);
	FILELIST *file = find_file(path);
printf("befor update permission:\n");
print_file(file);
	if (file == NULL) {
		PDEBUG("The file %s is opened by other client now, can't unlink!\n", path);
		*res = -1;
		return TRUE;
	}
	if (arg->mode == RTOW) {
		printf("update RTOW\n");
		CLIPER *p;
		if (file->rcount == 1 && (p = find_client(file->rlist, arg->ip)) != NULL) /*file->rlist != NULL && file->rlist.ip == arg->ip)*/ {
			delete_client(&file->rlist, p);
			file->rcount = 0;
			file->wcount = 1;
			file->wip = arg->ip;
			*res = 0;
		}
		else if (file->wcount > 0 && file->wip == arg->ip) {//client写操作已完成，他在向MDS释放锁之前，又发起写操作
			//nothing to do here
			*res = 0;
		}
		else {
			*res = -EAGAIN;
			printf("update failed\n");
			print_file(file);
		}
	}
	else if (arg->mode == WTOR) {
		printf("update WTOR\n");
		if (file->wcount == 1 && file->wip == arg->ip) {
			CLIPER *p;
			file->wip = 0;
			file->wcount = 0;
			
			file->rcount++;
			p = (CLIPER *) malloc (sizeof(CLIPER));
			p->ip = arg->ip;
			p->next = p->pre = NULL;
			insert_client(&file->rlist, p);
			*res = 0;			
		}
		else {
			*res = -EAGAIN;
			printf("update failed\n");
			print_file(file);
		}
	}
	else {
		printf("not handle here\n");
		*res = 0;
	}
printf("after update permission:\n");
print_file(file);
	PDEBUG("[update_permission]res = %d\n", *res);
	return TRUE;
}

bool_t cs_mkdir_2_svc(mkdir_in *arg, int *res, struct svc_req *rqst)
{
	PDEBUG("[mkdir_2_svc]: path = %s, mode = %d\n", arg->filename, arg->mode);
	char path[NAMELEN];
	sprintf(path, "%s%s", METAROOT, arg->filename);
	
	int mode = arg->mode;

    	*res = mkdir(path, mode);
    	if (*res == -1)
        	*res = -errno;

	return TRUE;
}

bool_t cs_rmdir_2_svc(rmdir_in *arg, int *res, struct svc_req *rqst)
{
	PDEBUG("[rmdir_2_svc]: path = %s\n", arg->filename);
	char path[NAMELEN];
	sprintf(path, "%s%s", METAROOT, arg->filename);

    	*res = rmdir(path);
    	if (*res == -1)
        	*res = -errno;

	return TRUE;
}

bool_t cs_rename_2_svc(cs_rename_in *arg, int *res, struct svc_req *rqst)
{
	PDEBUG("[rename] from %s to %s\n", arg->from, arg->to);
	char frompath[NAMELEN];
	char topath[NAMELEN];
	sprintf(frompath, "%s%s", METAROOT, arg->from);
	sprintf(topath, "%s%s", METAROOT, arg->to);
	
	FILELIST *file = find_file(frompath);
	if (file == NULL) {//the file is not opened now
		*res = rename(frompath, topath);
    		if (*res == -1) {
			printf("rename failed:%s\n", strerror(errno));
			*res = -errno;
		}
	}
	else {
		*res = -EAGAIN;
	}
        return TRUE;	
}

bool_t os_loadreport_2_svc(os_loadreport_in *arg, int *res, struct svc_req *rqst)
{
	PDEBUG("[Loadreport]:%u\n", arg->ipn);
	struct nodelist *osd = find_node(arg->ipn);
	if (osd == NULL) {
		PDEBUG("OSD %u not exist\n", arg->ipn);
		*res = insert_osd(arg);		
	}
	else {
		*res = update_osd(osd, arg);
	}
	print_nodelist();
	return TRUE;
}


int cs_2_freeresult(SVCXPRT *transp, xdrproc_t xdr_result, caddr_t result)
{
	xdr_free(xdr_result, result);
	return 1;
}

int os_2_freeresult(SVCXPRT *transp, xdrproc_t xdr_result, caddr_t result)
{
	xdr_free(xdr_result, result);
	return 1;
}

